/*
 *******************************************************************************
 *
 * Copyright (C) 2019-2020 Dialog Semiconductor.
 * This computer program includes Confidential, Proprietary Information
 * of Dialog Semiconductor. All Rights Reserved.
 *
 *******************************************************************************
 */

package com.dialog.suotalib.suota;

import static com.dialog.suotalib.global.SuotaProfile.Constants.MEMORY_TYPE_EXTERNAL_I2C;
import static com.dialog.suotalib.global.SuotaProfile.Constants.MEMORY_TYPE_EXTERNAL_SPI;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_DEF_SVC1_RX_UUID_128;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_DEF_SVC1_TX_UUID_128;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_FIRMWARE_REVISION_STRING;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_HARDWARE_REVISION_STRING;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_IEEE_11073;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_MANUFACTURER_NAME_STRING;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_MODEL_NUMBER_STRING;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_PNP_ID;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_SERIAL_NUMBER_STRING;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_SOFTWARE_REVISION_STRING;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CHARACTERISTIC_SYSTEM_ID;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.CLIENT_CONFIG_DESCRIPTOR;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.DEF_SERVICE_SVC1_UUID_128;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.SUOTA_L2CAP_PSM_UUID;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.SUOTA_MTU_UUID;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.SUOTA_PATCH_DATA_CHAR_SIZE_UUID;
import static com.dialog.suotalib.global.SuotaProfile.Uuid.SUOTA_VERSION_UUID;

import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;

import com.chaquo.python.PyObject;
import com.dialog.suotalib.global.SuotaLibConfig;
import com.dialog.suotalib.global.SuotaLibConfig.Default;
import com.dialog.suotalib.global.SuotaLibLog;
import com.dialog.suotalib.global.SuotaProfile;
import com.dialog.suotalib.ring.RingPythonManagement;
import com.dialog.suotalib.utils.FileUtils;
import com.dialog.suotalib.utils.L;
import com.dialog.suotalib.utils.MainThread;

import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

public class SuotaManager implements LifecycleObserver {
    private static final String TAG = "SuotaManager";
    private BluetoothGattCharacteristic textTXCharacteristic;
    private BluetoothGattCharacteristic textRXCharacteristic;
    private BluetoothGattDescriptor testDescriptor;


    public enum ManagerState {
        /**
         * 断开链接
         */
        DISCONNECTED,
        /**
         * 连接中
         */
        CONNECTING,
        /**
         * 已连接
         */
        CONNECTED
    }

    private Context context;
    private BluetoothDevice device;
    private SuotaManagerCallback suotaManagerCallback;
    private BluetoothGatt gatt;
    private GattCallback gattCallback;
    private ManagerState state;
    private WeakReference<Context> uiContextWeakReference;

    private LinkedList<GattOperation> gattQueue = new LinkedList<>();
    /**
     * GATA是否挂起
     */
    private boolean gattOperationPending;
    private final Object gattQueueLock = new Object();

    private boolean refreshOnConnectionCalled;
    private boolean refreshAfterUpdatePending;
    private boolean rebootSent;
    private boolean requestConnectionPriority = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && SuotaLibConfig.REQUEST_CONNECTION_PRIORITY;

    // SUOTA configuration
    private int blockSize = Default.BLOCK_SIZE;
    private int chunkSize = Default.CHUNK_SIZE;
    private int imageBank = Default.IMAGE_BANK;
    private int memoryType = Default.MEMORY_TYPE;
    // SPI
    private int misoGpio = Default.MISO_GPIO;
    private int mosiGpio = Default.MOSI_GPIO;
    private int csGpio = Default.CS_GPIO;
    private int sckGpio = Default.SCK_GPIO;
    // I2C
    private int i2cDeviceAddress = Integer.decode(Default.I2C_DEVICE_ADDRESS);
    private int sclGpio = Default.SCL_GPIO;
    private int sdaGpio = Default.SDA_GPIO;

    // region Characteristics references, values and have read flags
    // Suota Support
    private BluetoothGattService suotaService;
    private BluetoothGattCharacteristic memDevCharacteristic;
    private BluetoothGattCharacteristic gpioMapCharacteristic;
    private BluetoothGattCharacteristic memoryInfoCharacteristic;
    private BluetoothGattCharacteristic patchLengthCharacteristic;
    private BluetoothGattCharacteristic patchDataCharacteristic;
    private BluetoothGattCharacteristic serviceStatusCharacteristic;
    private BluetoothGattDescriptor serviceStatusClientConfigDescriptor;

    // Suota Info
    private BluetoothGattCharacteristic suotaVersionCharacteristic;
    private BluetoothGattCharacteristic patchDataSizeCharacteristic;
    private BluetoothGattCharacteristic mtuCharacteristic;
    private BluetoothGattCharacteristic l2capPsmCharacteristic;

    private int suotaVersion;
    private int mtu = SuotaProfile.DEFAULT_MTU;
    private int patchDataSize = Default.CHUNK_SIZE;
    private int l2capPsm;
    private boolean readSuotaVersion;
    private boolean readPatchDataSize;
    private boolean readMtu;
    private boolean readL2capPsm;

    // Device Info
    private BluetoothGattService deviceInfoService;
    private BluetoothGattCharacteristic manufacturerNameCharacteristic;
    private BluetoothGattCharacteristic modelNumberCharacteristic;
    private BluetoothGattCharacteristic serialNumberCharacteristic;
    private BluetoothGattCharacteristic hardwareRevisionCharacteristic;
    private BluetoothGattCharacteristic firmwareRevisionCharacteristic;
    private BluetoothGattCharacteristic softwareRevisionCharacteristic;
    private BluetoothGattCharacteristic systemIdCharacteristic;
    private BluetoothGattCharacteristic ieee11073Characteristic;
    private BluetoothGattCharacteristic pnpIdCharacteristic;
    //测试
    private BluetoothGattService testService;
    private String manufacturer;
    private String modelNumber;
    private String serialNumber;
    private String hardwareRevision;
    private String firmwareRevision;
    private String softwareRevision;
    private byte[] systemId;
    private byte[] ieee11073;
    private byte[] pnpId;

    // endregion

    private HashMap<UUID, BluetoothGattCharacteristic> suotaInfoMap;
    private HashMap<UUID, BluetoothGattCharacteristic> deviceInfoMap;
    private final Object suotaInfoMapLock = new Object();
    private final Object deviceInfoMapLock = new Object();
    private boolean isSuotaInfoReadGroupPending;
    private AtomicBoolean isDeviceInfoReadGroupPending;
    private int totalSuotaInfo;
    private int totalDeviceInfo;

    private static final List<UUID> suotaInfoUuids = Arrays.asList(
            SUOTA_VERSION_UUID,
            SUOTA_PATCH_DATA_CHAR_SIZE_UUID,
            SUOTA_MTU_UUID,
            SUOTA_L2CAP_PSM_UUID);

    private static final List<UUID> deviceInfoUuids = Arrays.asList(
            CHARACTERISTIC_MANUFACTURER_NAME_STRING,
            CHARACTERISTIC_MODEL_NUMBER_STRING,
            CHARACTERISTIC_SERIAL_NUMBER_STRING,
            CHARACTERISTIC_HARDWARE_REVISION_STRING,
            CHARACTERISTIC_FIRMWARE_REVISION_STRING,
            CHARACTERISTIC_SOFTWARE_REVISION_STRING,
            CHARACTERISTIC_SYSTEM_ID,
            CHARACTERISTIC_IEEE_11073,
            CHARACTERISTIC_PNP_ID);

    // region Suota Manager API

    public SuotaManager(Context context, BluetoothDevice device, SuotaManagerCallback suotaManagerCallback) {
        this.context = context.getApplicationContext();
        this.device = device;
        this.suotaManagerCallback = suotaManagerCallback;
        state = ManagerState.DISCONNECTED;
        reset();
    }

    public SuotaManager(Lifecycle lifecycle, Context context, BluetoothDevice device, SuotaManagerCallback suotaManagerCallback) {
        this(context, device, suotaManagerCallback);
        registerLifecycle(lifecycle);
    }

    private void registerLifecycle(Lifecycle lifecycle) {
        lifecycle.addObserver(this);
    }

    // region Public Getters and Setters

    public ManagerState getState() {
        return state;
    }




    /**
     * Returns the connected Bluetooth Device
     *
     * @return bluetooth device
     */
    public BluetoothDevice getDevice() {
        return device;
    }

    /**
     * Returns the name of the connected Bluetooth Device
     *
     * @return name of bluetooth device
     */
    @SuppressLint("MissingPermission")
    public String getDeviceName() {
        return device != null ? device.getName() : "";
    }



    public BluetoothGattCharacteristic getMemDevCharacteristic() {
        return memDevCharacteristic;
    }

    public BluetoothGattCharacteristic getGpioMapCharacteristic() {
        return gpioMapCharacteristic;
    }

    public BluetoothGattCharacteristic getMemoryInfoCharacteristic() {
        return memoryInfoCharacteristic;
    }

    public BluetoothGattCharacteristic getPatchLengthCharacteristic() {
        return patchLengthCharacteristic;
    }

    public BluetoothGattCharacteristic getPatchDataCharacteristic() {
        return patchDataCharacteristic;
    }

    public BluetoothGattCharacteristic getServiceStatusCharacteristic() {
        return serviceStatusCharacteristic;
    }

    public BluetoothGattDescriptor getServiceStatusClientConfigDescriptor() {
        return serviceStatusClientConfigDescriptor;
    }

    public BluetoothGattCharacteristic getTextTXCharacteristic() {
        return textTXCharacteristic;
    }

    public void setTextTXCharacteristic(BluetoothGattCharacteristic textTXCharacteristic) {
        this.textTXCharacteristic = textTXCharacteristic;
    }

    public BluetoothGattCharacteristic getTextRXCharacteristic() {
        return textRXCharacteristic;
    }

    public BluetoothGattDescriptor getTestDescriptor() {
        return testDescriptor;
    }

    public void setTestDescriptor(BluetoothGattDescriptor testDescriptor) {
        this.testDescriptor = testDescriptor;
    }

    public void setTextRXCharacteristic(BluetoothGattCharacteristic textRXCharacteristic) {
        this.textRXCharacteristic = textRXCharacteristic;
    }




    /**
     * 链接蓝牙
     */
    @SuppressLint("MissingPermission")
    public void connect() {
        if (state != ManagerState.DISCONNECTED) {
            Log.e(TAG, "Cannot create new connection yet. The previous is still going!");
            return;
        }
        reset();
        gattCallback = new GattCallback(this);
        state = ManagerState.CONNECTING;
        if (Build.VERSION.SDK_INT < 23) {
            gatt = device.connectGatt(this.context, false, gattCallback);
        } else {
            gatt = device.connectGatt(this.context, false, gattCallback, BluetoothDevice.TRANSPORT_LE);
        }
    }


    /**
     * Disconnects from connected Bluetooth device
     */
    @SuppressLint("MissingPermission")
    public void disconnect() {
        if (state == ManagerState.DISCONNECTED)
            return;

        synchronized (gattQueueLock) {
            gattOperationPending = false;
            gattQueue.clear();
        }

        try {
            if (gatt == null)
                return;

            if (SuotaLibLog.MANAGER)
                Log.d(TAG, "Disconnecting from device...");
            gatt.disconnect();


            // handle case that trying to disconnect without having prior connected to device
            if (state == ManagerState.CONNECTING) {
                if (SuotaLibLog.MANAGER)
                    Log.d(TAG, "Closing Gatt...");
                gatt.close();
            }
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * Cleans up everything !! first disconnect
     * Call this when you are done with SuotaManager
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    public void destroy() {
        Log.d(TAG, "Destroy");
        if (state != ManagerState.DISCONNECTED) {
            disconnect();
        }

        synchronized (gattQueueLock) {
            gattOperationPending = false;
            gattQueue.clear();
        }

    }
    
    // endregion

    // endregion

    // region Package Private Methods
    //318767104
    int getMemoryDevice() {
        return (memoryType << 24) | imageBank;
    }

    int getGpioMap() {
        switch (memoryType) {
            case MEMORY_TYPE_EXTERNAL_SPI:
                return getSpiGpioMap(); //这
            case MEMORY_TYPE_EXTERNAL_I2C:
                return getI2cGpioMap();
            default:
                return 0;
        }
    }
    //misoGpio=19，mosiGpio=32，csGpio=17，sckGpio=4
    int getSpiGpioMap() {
        return (misoGpio << 24) | (mosiGpio << 16) | (csGpio << 8) | sckGpio;
    }

    int getI2cGpioMap() {
        return (i2cDeviceAddress << 16) | (sclGpio << 8) | sdaGpio;
    }

    public Context getContext() {
        return context;
    }

    SuotaManagerCallback getSuotaManagerCallback() {
        return suotaManagerCallback;
    }

    public void setUiContext(Context context) {
        uiContextWeakReference = new WeakReference<>(context);
    }


    boolean isRebootSent() {
        return rebootSent;
    }

    public BluetoothGatt getGatt() {
        return gatt;
    }

    void setGatt(BluetoothGatt gatt) {
        this.gatt = gatt;
    }

    void setState(ManagerState state) {
        this.state = state;
    }

    @SuppressLint("MissingPermission")
    void close() {
        Log.d(TAG, "Close");
        if (gatt == null)
            return;

        if (SuotaLibConfig.REFRESH_AFTER_UPDATE) {
            // Refresh device cache if update was successful
            if (refreshAfterUpdatePending) {
                refreshAfterUpdatePending = false;
                if (isRefreshRequired(device))
                    refreshDeviceCache(gatt);
            }
        }

        gatt.close();
        gatt = null;

        gattCallback.destroy();
        gattCallback = null;
    }

    /**
     * GATT队列
     * @param gattOperation
     */
    public void enqueueGattOperation(GattOperation gattOperation) {
        synchronized (gattQueueLock) {
            if (gattOperationPending) {
                gattQueue.add(gattOperation);
            } else {
                executeGattOperation(gattOperation);
            }
        }
    }

    void enqueueGattOperations(List<GattOperation> gattOperations) {
        synchronized (gattQueueLock) {
            gattQueue.addAll(gattOperations);
            if (!gattOperationPending) {
                dequeueGattOperation();
            }
        }
    }

    void dequeueGattOperation() {
        synchronized (gattQueueLock) {
            gattOperationPending = false;
            if (gattQueue.isEmpty())
                return;
            executeGattOperation(gattQueue.poll());
        }
    }

    private void executeGattOperation(@NonNull GattOperation gattOperation) {
        if (gatt == null) {
            failureNullGatt();
            return;
        }

        if (gattOperation.execute(gatt)) {
            gattOperationPending = true;
            if (gattOperation.getType() == GattOperation.OperationType.REBOOT_COMMAND) {
                rebootSent = true;
                MainThread.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        suotaManagerCallback.onRebootSent();
                    }
                });
            }
        } else {
            notifyFailure(gattOperation.getType() != GattOperation.OperationType.MTU_REQUEST ? SuotaProfile.Errors.GATT_OPERATION_ERROR : SuotaProfile.Errors.MTU_REQUEST_FAILED);
        }
    }


    // enregion
    //
    //    // region Gatt Callback Wrappersd

    /**
     * 发现服务回调
     * @param gatt
     */
    @SuppressLint("MissingPermission")
    void onServicesDiscovered(BluetoothGatt gatt) {
        MainThread.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                suotaManagerCallback.onServicesDiscovered(); //无用
            }
        });

        if (SuotaLibConfig.REFRESH_ON_CONNECTION) {  //链接超时刷新默认 true
            if (!refreshOnConnectionCalled) { //调用链接时刷新 初始化链接时 = false //记录如果连接超时一次刷新再次链接，再次链接失败 不再链接
                refreshOnConnectionCalled = true;
                if (isRefreshRequired(device) && refreshDeviceCache(gatt)) { //如果需要刷新并且刷新设备缓存是否成功
                    Log.d(TAG, "Restart discovery after refresh");
                    gatt.discoverServices(); //探索GATT服务
                    return;
                }
            }
        }

        initServices();  //获取所有服务
    }

    /**
     * 蓝牙接收读取特征信息回调
     * @param characteristic
     */
    void onCharacteristicRead(@NonNull final BluetoothGattCharacteristic characteristic) {

    }

    void onCharacteristicWrite(@NonNull BluetoothGattCharacteristic characteristic) {

    }

    void onCharacteristicChanged(@NonNull BluetoothGattCharacteristic characteristic, byte[] value) {

    }


    private String gustureData = "";
    public void onPythonData(String data) {
        if (TextUtils.isEmpty(data)) {
            return;
        }
        Log.e("onPythonData  手势模式isGoGusture=", "data" + data);
        String[] strings = data.split(",");
        //触摸“手”：0x 02 ：点击； 0x 06：长按； 0x01： 放开（其他值不予理睬）； 0x04：是点了（暂停键 >|| ）
        String guestureMode = strings[0].toString();
        Log.e("onPythonData  手势模式isGoGusture=", "data" + "guestureMode" + guestureMode);
        RingPythonManagement.getRingPythonManagement().setGoGusture(true);
        gustureData = data;
//        if (BaseApp.getInstance().isGoGusture()) {
        RingPythonManagement.getRingPythonManagement().setHaveGustureTime(System.currentTimeMillis());
        if (!TextUtils.isEmpty(guestureMode) && !"2".contains(guestureMode)) {
            //开启轮询
            if (!isRun) {
                L.i(TAG, "开启轮询");
                mHandler.postDelayed(runnable, 70);
            }
            if (!TextUtils.isEmpty(gustureData)) {
                L.i(TAG, "轮询run 手势模式写数据");
                if (TextUtils.isEmpty(SuotaLibConfig.getFileName())) {
                    SuotaLibConfig.setFileName(""+System.currentTimeMillis());

                }
                fileName = SuotaLibConfig.Default.SDPATH + SuotaLibConfig.getFileName() + ".txt";
                FileUtils.write(SuotaLibConfig.getMkDir(), SuotaLibConfig.getFileName(), gustureData);
            }
        }
    }    private Handler mHandler = new Handler();
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            L.i(TAG, "轮询run");
            isRun = true;
            if (System.currentTimeMillis() - RingPythonManagement.getRingPythonManagement().getHaveGustureTime() > 500) {
                SuotaLibConfig.setFileName("" + System.currentTimeMillis());
                L.i(TAG, "手势模式完成写数据,换数据名 结束轮询");

                removeHandler();
            } else {

                mHandler.postDelayed(this, 70);
            }
        }
    };  private String fileName = "";

    private void removeHandler() {
        if (mHandler != null && isRun) {
            sendPYthon(fileName,getpath() + "/save.pt" );
            isRun = false;
            mHandler.removeCallbacks(runnable);
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    FileUtils.delete();
                }
            },50);
        }
    }

    private String getpath() {
        return context.getFilesDir().getAbsolutePath();
    }

    private boolean isRun = false;
    private void sendPYthon(String datapath, String modelpath) {
        Log.e(" sendPYthon py   recognize 进入",
                "time ms=" + System.currentTimeMillis() / 1000 + " " + System.currentTimeMillis() % 1000);

        PyObject pyData = RingPythonManagement.getRingPythonManagement().getmPyObject().callAttr("entry", datapath, modelpath);
        int result = pyData.toJava(Integer.class);
        if (result == -1) {
            return;
        }
        Log.e(" onPythonData py   recognize 结果", result + "");


        MainThread.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                suotaManagerCallback.onPythonData(result);
//                suotaManagerCallback.onPythonData(data);
            }
        });


    }

    /**
     * 写入回调
     * @param descriptor
     */
    void onDescriptorWrite(@NonNull BluetoothGattDescriptor descriptor) {

        MainThread.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                suotaManagerCallback.complete();
            }
        });
    }



    void failureNullGatt() {
        notifyFailure(SuotaProfile.Errors.NULL_GATT_SINGLETON);
    }


    /**
     * 初始化所有服务
     */
    private void initServices() {
        testService= gatt.getService(DEF_SERVICE_SVC1_UUID_128);
        if (testService!=null){
           textTXCharacteristic = testService.getCharacteristic(CHARACTERISTIC_DEF_SVC1_TX_UUID_128);
           textRXCharacteristic = testService.getCharacteristic(CHARACTERISTIC_DEF_SVC1_RX_UUID_128);
            if (textTXCharacteristic != null)
                testDescriptor = textTXCharacteristic.getDescriptor(CLIENT_CONFIG_DESCRIPTOR);
        }
        if (testService!=null&&textTXCharacteristic!=null&&textRXCharacteristic!=null&&testDescriptor!=null){
            byte[] data = {(byte)0x5a, (byte) 0xa5, (byte) 0x83, (byte) 0xff};
            enqueueGattOperation(new GattOperation(getTextRXCharacteristic(),data));
            @SuppressLint("MissingPermission")
            boolean isEnableNotification =  gatt.setCharacteristicNotification(getTextTXCharacteristic(), true); //设置蓝牙通知特征为（服务状态特征）
            Log.e("isEnableNotification","isEnableNotification:"+isEnableNotification);
//        //发送客户端配置描述，并启用通知
            enqueueGattOperation(new GattOperation(getTestDescriptor(), BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE));

        }



    }



    /**
     * 重置链接参数
     */
    private void reset() {
        synchronized (gattQueueLock) {
            gattOperationPending = false;
            gattQueue.clear();
        }

        synchronized (suotaInfoMapLock) {
            suotaInfoMap = new HashMap<>();
            totalSuotaInfo = 0;
        }

        synchronized (deviceInfoMapLock) {
            deviceInfoMap = new HashMap<>();
            totalDeviceInfo = 0;
        }

        suotaService = null;
        memDevCharacteristic = null;
        gpioMapCharacteristic = null;
        memoryInfoCharacteristic = null;
        patchLengthCharacteristic = null;
        patchDataCharacteristic = null;
        serviceStatusCharacteristic = null;
        serviceStatusClientConfigDescriptor = null;
        suotaVersionCharacteristic = null;
        patchDataSizeCharacteristic = null;
        mtuCharacteristic = null;
        l2capPsmCharacteristic = null;

        deviceInfoService = null;
        manufacturerNameCharacteristic = null;
        modelNumberCharacteristic = null;
        serialNumberCharacteristic = null;
        hardwareRevisionCharacteristic = null;
        firmwareRevisionCharacteristic = null;
        softwareRevisionCharacteristic = null;
        systemIdCharacteristic = null;
        ieee11073Characteristic = null;
        pnpIdCharacteristic = null;

        suotaVersion = -1;
        mtu = SuotaProfile.DEFAULT_MTU;
        patchDataSize = Default.CHUNK_SIZE;
        chunkSize = Default.CHUNK_SIZE;
        l2capPsm = -1;
        readSuotaVersion = false;
        readPatchDataSize = false;
        readMtu = false;
        readL2capPsm = false;

        manufacturer = null;
        modelNumber = null;
        serialNumber = null;
        hardwareRevision = null;
        firmwareRevision = null;
        softwareRevision = null;
        systemId = null;
        ieee11073 = null;
        pnpId = null;

        refreshOnConnectionCalled = false;
        refreshAfterUpdatePending = false;
        rebootSent = false;
        isDeviceInfoReadGroupPending = new AtomicBoolean();
        isSuotaInfoReadGroupPending = false;
    }


    private void notifyFailure(final int value) {
        MainThread.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                suotaManagerCallback.onFailure(value);
            }
        });
    }







    /**
     * 判断是否需要刷新
     * @param device
     * @return
     */
    private static boolean isRefreshRequired(BluetoothDevice device) {
        if (SuotaLibConfig.REFRESH_REQUIRED)
            return true;
        if (!SuotaLibConfig.REFRESH_REQUIRED_IF_BONDED)
            return false;
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        return bluetoothAdapter != null && bluetoothAdapter.getBondedDevices().contains(device); //先判断当前的手机手否存在蓝牙 并且该设备是否在绑定的设备名单中
    }

    /**
     * 刷新设备缓存
     * @param gatt
     * @return
     */
    private static boolean refreshDeviceCache(@NonNull BluetoothGatt gatt) {
        Log.d(TAG, "Refresh device cache");
        try {
            Method refreshMethod = gatt.getClass().getMethod("refresh", (Class[]) null); //刷新设备缓存
            if (refreshMethod == null)
                return false;
            boolean result = (Boolean) refreshMethod.invoke(gatt, (Object[]) null);
            if (!result)
                Log.d(TAG, "Refresh failed");
            return result;
        } catch (Exception e) {
            Log.e(TAG, "An exception occurred while refreshing device cache", e);
        }
        return false;
    }






    //重启指令
    public void sendRebootCommand() {
        if (gatt == null) {
            failureNullGatt();
            return;
        }

        if (SuotaLibLog.MANAGER)
            Log.d(TAG, "Send SUOTA reboot command");

        enqueueGattOperation(new GattOperation(GattOperation.OperationType.REBOOT_COMMAND, memDevCharacteristic, SuotaProfile.Constants.SUOTA_REBOOT));
    }

    // endregion
}
